#include "wfw.hpp"
#include <algorithm>
#include <stdint.h>
#include <stdio.h>

namespace Trade {

//
// 5 minutes for somethign wrong
//
bool
WaitingForWrong::valied() noexcept
{
  return ((tm_cls_near != 0) || (tm_cls_far != 0) || (tm_vss_near != 0) ||
          (tm_vss_far != 0) || (max_amb_time != 0) || (min_g2_time != 0));
}

static const int hour_2 = 3600 * 2;

bool
WaitingForWrong::far_more_2_hours(const time_t tm) noexcept
{
  bool good = false;
  good = (tm_vss_far && ((tm_vss_far - tm) >= hour_2));
  if (!good && !tm_vss_far)
    good = (tm_cls_far && ((tm_cls_far - tm) >= hour_2));
  if (!good && !tm_vss_far && !tm_cls_far)
    good = (max_amb_time && ((max_amb_time - tm) >= hour_2));
  if (!good && !tm_vss_far && !tm_cls_far && !max_amb_time)
    good = (min_g2_time && ((min_g2_time - tm) >= hour_2));
  return good;
}

bool
WaitingForWrong::in_far_timer(const time_t tm) noexcept
{
  bool good = (tm_vss_far && tm_vss_far > tm);
  if (!good && !tm_vss_far) {
    good = (tm_cls_far && (tm_cls_far > tm));
  }
  if (!good && !tm_vss_far && !tm_cls_far) {
    good = (max_amb_time && (max_amb_time > tm));
  }
  if (!good && !tm_vss_far && !tm_cls_far && !max_amb_time) {
    good = (min_g2_time && (min_g2_time > tm));
  }
  return good;
}

uint8_t
WaitingForWrong::timer_out(const time_t tm) noexcept
{
  uint8_t o = to;
  // if (vt_cls_near && !(to & 1) && (tm >= vt_cls_near))
  //   to |= 1;
  if (tm_cls_far && !(to & 2) && (tm >= tm_cls_far))
    to |= 2;
  // if (vt_vss_near && !(to & 4) && (tm >= vt_vss_near))
  //   to |= 4;
  if (tm_vss_far && !(to & 8) && (tm >= tm_vss_far))
    to |= 8;
  if (max_amb_time && !(to & 0x10) && (tm >= max_amb_time))
    to |= 0x10;
  if (min_g2_time && !(to & 0x20) && (tm >= min_g2_time))
    to |= 0x20;

  if (o != to) {
    return (o ^ to);
  }
  return (0);
}

void
WaitingForWrong::init_time_out(const time_t tm) noexcept
{
  timer_out(tm);
}

bool
WaitingForWrong::timer_outed(const time_t tm) noexcept
{
  if (!to) {
    if (0 == (tm_cls_near + tm_cls_far + tm_vss_near + tm_vss_far +
              max_amb_time + min_g2_time))
      return true;
    else
      return false;
  }
  return to;
}

bool
WaitingForWrong::g2_timer_outed(const time_t t_now) const noexcept
{
  return (g2_time - t_now < MINUTES_45);
}

bool
WaitingForWrong::fs_work_changed(const time_t t_now) noexcept
{
  bool work = ((fs_time - t_now >= MINUTES_15) &&
               ((!tm_cls_near && !tm_cls_far) ||
                (fs_time < tm_cls_near && fs_time + MINUTES_15 < tm_cls_far)));
  if (fs_work != work) {
    fs_work = work;
    if (work)
      return true;
  }
  return false;
}

bool
WaitingForWrong::fs_is_intered() const noexcept
{
  return (fs_dir && fs_type == 2);
}

bool
WaitingForWrong::fs_good_for_dir(const dir_t d) const noexcept
{
  return fs_work && fs_dir == d && fs_type == 1;
}

void
WaitingForWrong::print(const time_t tm, const price_t hp_now) noexcept
{
  printf("\033[38;5;46m-- WFW -- \033[0m");
  printf("d%d t%d %d %.02lf(%.02lf); ",
         fs_dir,
         fs_type,
         (fs_time ? int(fs_time - tm) / 60 : 0),
         (fs_hpx - hp_now),
         fs_hpx);
  printf("%d ", (tm_cls_near ? int(tm_cls_near - tm) / 60 : 0));
  printf("%d %d;", (tm_cls_far ? int(tm_cls_far - tm) / 60 : 0), cls_dir);
  printf("%d ", (tm_vss_near ? int(tm_vss_near - tm) / 60 : 0));
  printf("%d %d;", (tm_vss_far ? int(tm_vss_far - tm) / 60 : 0), vss_dir);
  printf("AMB %d ", (max_amb_time ? int(max_amb_time - tm) / 60 : 0));
  printf("G2 %d %d %d | %.02lf (%.02lf)\n",
         (g2_time ? int(g2_time - tm) / 60 : 0),
         g2_dir,
         g2_type,
         (hp_now - g2_hpx),
         g2_hpx);
}

int64_t
WaitingForWrong::now_timer(const dir_t d, const time_t tm) noexcept
{
  int64_t ret = 0;
  if ((tm_vss_far - tm >= 10 * 60))
    ret = tm_vss_far;
  else if (tm_cls_far - tm >= 10 * 60)
    ret = tm_cls_far;
  else if ((g2_time - tm >= 10 * 60) && d == g2_dir)
    ret = g2_time;
  else if (max_amb_time - tm >= 10 * 60)
    ret = max_amb_time;
  return ret;
}

int8_t
WaitingForWrong::is_not_good_for(const dir_t d, const time_t tm) const noexcept
{
  int8_t case_no = 0;

  if (g2_dir && g2_dir != d && (tm_vss_far - tm) < 3600 &&
      (tm_cls_far - tm) < 3600)
    case_no = 1;
  return case_no;
}

time_t
WaitingForWrong::get_timer_by_3same(const time_t t_now) noexcept
{
  time_t timer = 0;
  if (tm_vss_far - t_now > MINUTES_10) {
    timer = tm_vss_far;
  } else if (tm_cls_far - t_now > MINUTES_10) {
    timer = tm_cls_far;
  } else if (g2_time - t_now > AN_HOUR) {
    timer = g2_time;
  }
  return timer;
}

bool
WaitingForWrong::good_g2_and_vss(const dir_t vss_dir,
                                 const time_t t_now) noexcept
{
  return (g2_dir == vss_dir && g2_time > tm_vss_far &&
          (tm_vss_far - t_now >= MINUTES_10));
}

bool
WaitingForWrong::pass_one_go_two(const time_t t_now) noexcept
{
  if ((tm_vss_far - t_now < 0) && (tm_cls_far - t_now >= MINUTES_10)) {
    return true;
  } else {
    return false;
  }
}

time_t
WaitingForWrong::get_timer(const Wave& w,
                           const dir_t d,
                           const time_t t_now) noexcept
{
  time_t t = 0;

  auto compare_to_g2 = [this, t_now](const time_t tm) {
    time_t t = 0;
    if (tm < g2_time) {
      t = tm;
    } else {
      t = MINUTES_45 + t_now;
    }
    return t;
  };

  if (!tm_cls_far && !tm_vss_far) {
    if (fs_dir == d && fs_type == 1) {
      t = fs_time;
    }
  } else {
    if (!t) {
      if (w.have_vss_spp && w.solved_vss_spp == d) {
        if (tm_vss_far > MINUTES_10) {
          t = compare_to_g2(tm_vss_far);
          // t = tm_vss_far;
        } else if ((tm_vss_near - t_now >= MINUTES_10)) {
          t = compare_to_g2(tm_vss_near);
          // t = tm_vss_near;
        }
      }
    }

    if (w.have_cls_spp && d == w.solved_cls_spp) {
      if ((tm_cls_near - t_now) >= MINUTES_10) {
        t = compare_to_g2(tm_cls_near);
        // t = tm_cls_near;
      } else if ((tm_cls_far - t_now) >= MINUTES_10) {
        t = compare_to_g2(tm_cls_far);
        // t = tm_cls_far;
      }
    }
  }

  if (!t)
    t = MINUTES_45 + t_now;

  return t;
}

time_t
WaitingForWrong::get_abs_timer(const Wave& w,
                               const dir_t d,
                               const time_t t_now) noexcept
{
  time_t t = 0;

  auto compare_to_g2 = [this, t_now](const time_t tm) {
    time_t t = 0;
    if (tm < g2_time) {
      t = tm;
    } else {
      t = MINUTES_45 + t_now;
    }
    return t;
  };

  if (!tm_cls_far && !tm_vss_far) {
    if (fs_dir == d && fs_type == 1) {
      t = fs_time;
    }
  } else {
    if (!t) {
      if (w.have_vss_spp && w.solved_vss_spp == d) {
        if (tm_vss_far > MINUTES_10) {
          t = compare_to_g2(tm_vss_far);
          // t = tm_vss_far;
        } else if ((tm_vss_near - t_now >= MINUTES_10)) {
          t = compare_to_g2(tm_vss_near);
          // t = tm_vss_near;
        }
      }
    }

    if (w.have_cls_spp && d == w.solved_cls_spp) {
      if ((tm_cls_near - t_now) >= MINUTES_10) {
        t = compare_to_g2(tm_cls_near);
        // t = tm_cls_near;
      } else if ((tm_cls_far - t_now) >= MINUTES_10) {
        t = compare_to_g2(tm_cls_far);
        // t = tm_cls_far;
      }
    }
  }

  return t;
}

time_t
WaitingForWrong::get_abs_weak_timer(const Wave& w,
                                    const StageDirs& dirs,
                                    const dir_t d,
                                    const time_t t_now) noexcept
{
  time_t t = 0;

  {
    if (vss_dir == d && w.have_vss_spp && !w.solved_vss_spp &&
        (tm_vss_far > t_now)) {
      t = tm_vss_far;
    } else if (cls_dir == d && w.have_cls_spp && !w.solved_cls_spp &&
               (tm_cls_near > t_now)) {
      t = tm_cls_near;
    }
  }

  if (!t) {
    if ((g2_dir == d || dirs.mid_dir == d || dirs.ein_dir == d) &&
        (vss_dir == d)) {
      if (tm_vss_near > t_now)
        t = tm_vss_near;
    }
  }

  if (!t) {
    if (g2_dir == d)
      t = g2_time;
  }

  return t;
}

time_t
WaitingForWrong::get_weak_timer(const Wave& w,
                                const StageDirs& dirs,
                                const dir_t d,
                                const time_t t_now) noexcept
{
  time_t t = 0;

  {
    if (vss_dir == d && w.have_vss_spp && !w.solved_vss_spp &&
        (tm_vss_far > t_now)) {
      t = tm_vss_far;
    } else if (cls_dir == d && w.have_cls_spp && !w.solved_cls_spp &&
               (tm_cls_near > t_now)) {
      t = tm_cls_near;
    }
  }

  if (!t) {
    if ((g2_dir == d || dirs.mid_dir == d || dirs.ein_dir == d) &&
        (vss_dir == d)) {
      if (tm_vss_near > t_now)
        t = tm_vss_near;
    }
  }

  if (!t) {
    if (g2_dir == d)
      t = g2_time;
  }

  if (!t) {
    if (dirs.big_dir == d) {
      t = t_now + MINUTES_45;
    }
  }

  if (!t) {
    if (g2_timer_outed(t_now) && (g2_dir == -d) &&
        (g2_type == 2 || g2_type == -2)) {
      t = t_now + MINUTES_45;
    }
  }

  // if (!t && must_return)
  //   t = t_now + MINUTES_45;

  return t;
}

} // namespace Trade
